package xinbloom

import (
	"strconv"
	"strings"
	"sync"
)

const bitsPerWord = 64 // 存储数组，每个元素可以存储多少位。

type bitmap struct {
	sync.Mutex
	data []uint64 // 存储数组，每个 uint64 可以存储 64 位
}

// newBitMap 新建一个位图
// size 位图内有多少位
func newBitMap(size int) *bitmap {
	// 计算有几个元素，向上取整
	// size = 0，最少一个元素。也就是最少一个 uint64
	wordCount := (size + bitsPerWord - 1) / bitsPerWord
	return &bitmap{data: make([]uint64, wordCount)}
}

// set 设置位图索引位置为 1
// index 索引
func (b *bitmap) set(index uint64) {
	// 假设 index = 10

	// wordIndex = 0 , 即当前 index 位，需要修改的元素在第 0 个。
	wordIndex := index / bitsPerWord

	// bitIndex = 10 , 即当前 index 位，在第 0 个元素的第 10 位。
	bitIndex := index % bitsPerWord

	// b.data[wordIndex] |= (1 << bitIndex) 等价于下面：
	// b.data[wordIndex] = b.data[wordIndex] ｜ (1 << bitIndex)

	// (1 << bitIndex) => 1 左移 10 位的结果是 0000 ... 0010 0000 0000，此处忽略了好多位，一共是64位，从右侧数第 10 位为 1。十进制表示结果为 1024
	// b.data[wordIndex] 此时为0，二进制就是 64 个 0，

	// 0 ｜ 1024 ，二进制表示就是：0000 ... 0000 ｜ 0000 ... 0010 0000 0000,
	// ｜ 操作符的意思就是两个数，只要有一个 1 结果就是 1，其他的情况都是 0
	// 所以 0 ｜ 1024 = 1024

	// 最终结果：数组的第一个元素的二进制为：
	// 0000 ... 0010 0000 0000

	// 实现第 10 个索引位置从 0 修改为 1
	// b.data[wordIndex] |= (1 << bitIndex)
	b.Lock()
	defer b.Unlock()
	b.data[wordIndex] = b.data[wordIndex] | (1 << bitIndex)
}

// clear 将位图索引位置设置为 0
// index 索引
func (b *bitmap) clear(index uint64) {
	wordIndex := index / bitsPerWord
	bitIndex := index % bitsPerWord

	// 0000 ... 0010 0000 0000 & ^0000 ... 0010 0000 0000
	// ^0000 ... 0010 0000 0000 取反
	// 0000 ... 0010 0000 0000 & 1111 ... 1101 1111 1111
	// & 都为 1 才为 1
	// 0000 ... 0000 0000 0000

	b.Lock()
	defer b.Unlock()
	b.data[wordIndex] = b.data[wordIndex] & ^(1 << bitIndex)
}

// Test 判断位图索引位置是否为 1
func (b *bitmap) test(index uint64) bool {
	// index = 10

	// wordIndex = 1
	wordIndex := index / bitsPerWord
	// bitIndex = 10
	bitIndex := index % bitsPerWord

	// & 都为 1 才为 1
	// 1024 & 1024
	// 0000 ... 0010 0000 0000 & 0000 ... 0010 0000 0000
	// 第 10 位都为 1，其他位依然为 0，结果仍然是 1024

	// &
	return b.data[wordIndex]&(1<<bitIndex) != 0
}

func (b *bitmap) size() uint64 {
	return uint64(len(b.data))
}

func (b *bitmap) String() string {
	var str string

	for _, v := range b.data {
		str += "\n" + intToPaddedBinary(v)
	}

	return str
}

func intToPaddedBinary(num uint64) string {
	binaryStr := strconv.FormatUint(num, 2) // 将整数转换为二进制字符串
	padding := 64 - len(binaryStr)          // 计算需要补零的数量
	if padding > 0 {
		// 使用strings.Repeat生成指定数量的"0"，然后拼接在二进制字符串前面
		binaryStr = strings.Repeat("0", padding) + binaryStr
	}
	return binaryStr
}
